import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'


def try_1():
    """
    演示图 和 操作对象
    :return: 
    """
    # a = 10
    # b = 10
    # c = a + b
    # print(c)
    # 一个常量
    a = tf.constant(10)
    b = tf.constant(20)
    # 定义了一种操作 op
    c = tf.add(a, b)
    print(c)
    # 获取默认已经注册的图
    g = tf.get_default_graph()
    print(g)
    print("c操作对象的图:", c.graph)
    print(a.graph)
    print(b.graph)

    new_g = tf.Graph()
    print("手动创建的图: ", new_g)

    with new_g.as_default():
        # 在这个作用域中, 定义tensor类型的对象的图就是new_g
        d = tf.constant(20)
        print("=====", d.graph)

    # 使用的是系统的默认创建的图对象
    # 创建会话对象的时候 可以指定图对象
    with tf.Session(graph=new_g) as sess:
        # sess的graph是默认的图对象
        print(sess.graph)
        # ret = sess.run(c)
        # print(ret)
        # d是手动创建的图对象中的元素
        sess.run(d)


def sess_run():
    """
    会话对象和它的run方法
    :return: None
    """
    a = tf.constant(10)
    b = tf.constant(20, name='bname')
    f = tf.constant(30)
    # 定义了一种操作 op
    c = tf.add(a, b)
    # 定义一个占位符对象
    plt = tf.placeholder(tf.float32, shape=(2,2))
    print("=======", plt)

    print('++++++++++')
    print(a.graph)
    print(a.op)
    print(b.name)
    print(plt.shape)
    print(plt.get_shape)
    print('++++++++++')

    with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
        ret = sess.run([a, b])
        print(a.eval())
        """
        Device mapping: no known devices.
        Add: (Add): /job:localhost/replica:0/task:0/cpu:0
        Const_1: (Const): /job:localhost/replica:0/task:0/cpu:0
        Const: (Const): /job:localhost/replica:0/task:0/cpu:0
        """
        ret = sess.run(plt, feed_dict={plt:[[11.0,22.0],[33.0,44.0]]})
        print(ret)

    return None



def tf_shape():
    """
    张量  Tensor 多维的数组
    张量包含的属性: 图, op, 名字, 形状
    # 表示形状的时候 如果有None 形状待定, 最后在run的时候 指定数据就能够确定张量的形状
    # 在形状没有确定之前可以通过set_shape来指定形状 
    # 在张量的形状已经确定的情况下不能够通过set_shape修改
    # 张量的静态形状已经固定 就不能够再被修改, 如果没有固定可以通过set_shape修改形状,但是需要和已知的行或者列对应\
    # reshape: 1. 会生成新的对象  2. 动态创建新张量时，元素个数必须得匹配元素的个数  2行3列的数组在修改形状的时候 必须修改为3行两列

    :return: 
    """
    a = tf.constant(10)
    print(a.graph)
    print(a.op)
    print(a.name)
    print(a.shape)
    b = tf.constant(20, name='bname')
    f = tf.constant(30)
    # 定义了一种操作 op
    c = tf.add(a, b)
    # 定义一个占位符对象

    plt = tf.placeholder(tf.float32, shape=(2, 3))
    # plt = tf.placeholder(tf.float32, shape=(None, 3))
    # plt.set_shape([3, 3])
    print('++++++++++')
    print(plt.get_shape)
    print('++++++++++')

    # reshape 重新修改形状
    # reshape 修改形状的时候会生成新的对象
    print(plt.get_shape)
    re_plt = tf.reshape(plt, shape=[3, 2])
    print(re_plt.get_shape)

    with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
        ret = sess.run(plt, feed_dict={plt: [[11.0, 22.0, 33.0], [33.0, 44.0,55.0]]})
        ret1 = sess.run(re_plt, feed_dict = {re_plt : [[11.0, 22.0], [33.0, 44.0], [66.0,88.0]]})
        print(ret)
        print(ret1)


def var_demo():
    """
    演示变量的基本使用:
    定义  tf.Variable() 仅仅完成了变量的定义
    初始化 必须将当前图中定义变量 通过global_variables_initializer 收集所有需要初始化的变量 得到一个op对象
    初始化: 必须在 会话对象中 运行 初始化的op对象才能够创建变量
    作用域
    :return: None
    """
    # 不能够写成创建或者初始化 而是设置一个变量
    # 变量也是张量
    a = tf.Variable(initial_value=1.0, name='axxx')
    b = tf.Variable(initial_value=1.0, name='bxxx')
    # print(a)
    # 会返回一个op操作, 该操作会获取到当前图中所有需要被创建的变量
    init_op = tf.global_variables_initializer()
    print("=====")
    print(init_op)
    print("=====")
    with tf.Session() as sess:
        # 在访问a之前 进行初始化的操作, 运行init_op 就可以了
        sess.run(init_op)
        print(a.eval())

    return None


def mat():
    """
    矩阵相乘
    :return: None 
    """
    # 1行 3列的张量
    var1 = tf.Variable(initial_value=[[1,2,3]], name="var1")
    # 3行 2列 张量
    var2 = tf.Variable(initial_value=[[4,5],[6,7],[8,9]], name='var2')
    # 定义一个矩阵相乘的操作
    mat_op = tf.matmul(var1, var2)
    # Attempting to use uninitialized value Variable
    # 变量需要被初始化
    init_op = tf.global_variables_initializer()

    with tf.Session() as sess:

        # 运行初始化的op对象
        sess.run(init_op)
        # 变量初始化之后 可以将变量的变化写入到一个events文件中
        tf.summary.FileWriter(logdir='/tmp/summary/test/', graph=sess.graph)
        ret = sess.run(mat_op)
        print(ret)

    return None




def linear():
    """
    通过tensorflow来手动实现线性回归
    相关的函数: matmul, square, reduce_mean, tf.train.GradientDescentOptimizer(learning_rate)
    y = 0.7 * x1 + 0.8
    :return: None
    """
    # 准备数据 这个自变量的值服从标准的正态分布
    # 指定作用域的命名空间
    with tf.variable_scope('data'):
        x = tf.random_normal(shape=[100,1], mean=0, stddev=1.0)
        # 目标值 权重的矩阵 乘以特征值的矩阵
        y = tf.matmul(x, [[0.7]]) + 0.8

    with tf.variable_scope('model'):
        # 随机的指定权重值weight 和 偏置值 bias, 最终在训练的过程中让随机指定weight 和bias 向 0.7 和 0.8靠近
        # weight就应该是一个变量, 变量才能够被训练
        weight = tf.Variable(initial_value= tf.random_normal(shape=[1,1], mean=0, stddev=1.0), name='weight')
        bias = tf.Variable(initial_value=0.0, name='bias')

        # 获取预测值
        y_predict = tf.matmul(x, weight) + bias

    with tf.variable_scope("op"):
        # 获取均方误差
        loss = tf.reduce_mean(tf.square(y - y_predict))
        # 定义优化损失的op
        # 通过梯度下降优化误差 最小化误差
        # learning_rate 学习的速率
        train_op = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)
        # 需要初始化变量
        init_op = tf.global_variables_initializer()


    # 收集变量的变化
    tf.summary.scalar('loss', loss)
    tf.summary.scalar('bias', bias)
    tf.summary.histogram('weight', weight)
    # 合成一个操作对象
    merge_op = tf.summary.merge_all()

    # 定义模型的保存操作
    saver_op = tf.train.Saver()
    # 在会话中运行op 优化损失
    with tf.Session() as sess:
        # 初始化变量
        sess.run(init_op)
        # 将变量的变化写入到events文件中
        file_writer = tf.summary.FileWriter(logdir='/tmp/summary/linear/', graph=sess.graph)
        # 执行梯度下降优化损失的操作
        # 从之前保存的路径来进行恢复操作
        if os.path.exists('/tmp/summary/linear/'):
            print("===================")
            saver_op.restore(sess=sess, save_path='/tmp/summary/linear/li')
        for i in range(150):
            # 每次迭代都需要运行 需要在循环中执行
            summary = sess.run(merge_op)
            print("随机得到的权重值为%f, 偏置值为%f" % (weight.eval(), bias.eval()))
            sess.run(train_op)
            print("随机得到的权重值为%f, 偏置值为%f" % (weight.eval(), bias.eval()))
            # 每一次循环都需要记录变化\
            # 将合成的变化写入到events文件中
            file_writer.add_summary(summary, i)
            if i % 10 == 0:
                # 每次迭代都需要完成保存的操作
                pass
        # 迭代结束之后就开始保存变量
        saver_op.save(sess=sess,save_path='/tmp/summary/linear/li')
    return None




# 自定义命令行参数
tf.app.flags.DEFINE_string(flag_name='home_path',default_value='./tmp', docstring="默认的路径信息")
tf.app.flags.DEFINE_integer(flag_name='max_step',default_value=30, docstring="最大迭代的次数")
FLAGS = tf.app.flags.FLAGS

def main(argv):
    print(argv)
    print(FLAGS.home_path)
    print(FLAGS.max_step)

if __name__ == '__main__':
    # sess_run()
    # tf_shape()
    # var_demo()
    # mat()
    # linear()
    tf.app.run()